from django.shortcuts import render
from django import forms
from django.conf import settings
from django.core.files.storage import FileSystemStorage
from formtools.wizard.views import SessionWizardView

from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.template import RequestContext
from models import *
from forms import *
from forms import ACTION_FORM_MAP as act_map
from util import *

import datetime
import urllib2
import json
import traceback
import os

from django.http import HttpResponse

from pymongo import MongoClient
client = MongoClient()
db = client.htstool

BARCODE_DELIMITER = '_'
WIZARD_FORMS = [
	 ('experiment', ExpForm),
	 ('platelayout', PlateSettingForm),
	 ('upload', FileUpForm ),
	 ('upload_anno', AnnoUpForm ),
	 ('confirm', FileConfirmForm),
	 ('barcode', BarcodeForm),
		]

TEMPLATES = {"experiment":"import_wizard.html",
	     "platelayout":"select_ctrl.html",
	     "upload":"upload.html",
	     "upload_anno":"upload.html",
	     "confirm":"confirm.html",
	     "barcode":"barcode.html",
		}

########### views for displaying actions ##############
def home(request):
	# Get all experiments from DB
	exps = Experiment.objects.no_dereference()
	return render_to_response('index.html', {'Experiments': exps},
			      context_instance=RequestContext(request))

def expview(request, expid):
	exp = Experiment.objects(id=expid).select_related()[0]
	return render_to_response('experiment.html', {'exp': exp},
			      context_instance=RequestContext(request))

def grpview(request, grpid):
	group = PlateGroup.objects.get(id=grpid)
	return render_to_response('group.html', {'plategroup': group},
			      context_instance=RequestContext(request))



def plateview(request, plateid=''):
	plate = Plate.objects.get(id=plateid)
	g=PlateGroup.objects.get(plates=plate)
	exp=Experiment.objects.get(plategroups=g)
	
	if request.method == 'GET':		
		hits = request.GET.getlist('hit')
		if isinstance(plate, RPSuperPlateSummary):
			templ='rankprod_summary.html'
			pl_type='Rank Prod'
			df_tophits=plate.getTopHits()
			
			df_tophits.insert(len(df_tophits.columns), 'ind', df_tophits.index)

			#colnames = df_tophits.columns
			#rp_colnames = [u'RankProd', u'PFP', u'P-val']
			#repl_colnames = [cl for cl in colnames if not cl in rp_colnames]
			#
			#df_repls = df_tophits[repl_colnames]
			#df_rp = df_tophits[rp_colnames]
			#
			#json_repls=df_repls.to_json(orient='records')
			#json_rp=df_rp.to_json(orient='records')
			#index = json.dumps(df_repls.index.values.tolist())
			json_hits=df_tophits.to_json(orient='records')
			index = json.dumps(df_tophits.index.values.tolist())

			return render_to_response(templ, {'plate':plate, 'platetype':pl_type, 
				'screentype':exp.screentype, 'data':json_hits, 'index':index}, 
				context_instance=RequestContext(request))
		else:
			if isinstance(plate, RawPlate):
				templ='plate.html'
				pl_type='Raw data'
			elif isinstance(plate, NormPlate):
				templ='normplate.html'
				pl_type='Normalised data'
			elif isinstance(plate, SummPlate):
				templ='summplate.html'
				pl_type='Summarised data'
			elif isinstance(plate, RankProdPlate):
				templ='rankprod.html'
				pl_type='Rank Prod'

			return render_to_response(templ, {'plate':plate, 'platetype':pl_type, 
				'screentype':exp.screentype, 'hits':json.dumps(hits) },
					      context_instance=RequestContext(request))
	
	
########### views for creating new content actions ##############

#def createexpview(request):
#	if request.method == 'POST': 
#		# ContactForm was defined in the previous section
#		form = ExperimentForm(request.POST) # A form bound to the POST data
#		if form.is_valid(): # All validation rules pass
#			form.save()
#			return HttpResponseRedirect('/') 
#	else:
#		form = ExperimentForm() 
#
#	return render(request, 'experimentform.html', { 'form': form, })

def chgrpname(request):
	if request.method == 'POST': 
		gid=request.POST['id']
		txt=request.POST['value']
		attrname = request.POST['attrname']
		g=PlateGroup.objects.get(id=gid)
		setattr(g, attrname, txt)
		g.save()
		return HttpResponse(txt)


########### group some plates and select an action ##############
def groupplates(request):
	if request.method == 'GET':
		gid=request.GET['groupid']
		g = PlateGroup.objects.get(id=gid)
		plates = [{'pname':p.platename, 
				'conc':p.concentration, 'lib':p.lib, 
				'cell':p.cellline, 'cond':p.condition, 
				'repl':p.replicate, 'plateid':str(p.id)} for p in g.plates]
		json_data = json.dumps(plates)
		
		selectform = SelectActionForm()
	return render(request, 'grouping.html', { 'json': json_data, 'form': selectform })




########### import wizard view ##############
class ImportWizard(SessionWizardView):
	file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'raw_upload_files'))


	def get_template_names(self):
		return [TEMPLATES[self.steps.current]]

	def get_context_data(self, form, **kwargs):
		context = super(ImportWizard, self).get_context_data(form=form, **kwargs)
		if self.steps.current == 'confirm':
			# get the plate setting, dim of the plate
			setting = self.get_cleaned_data_for_step('platelayout')
			size = setting['size']

			# get the uploaded raw file
			upload_form_data = self.get_cleaned_data_for_step('upload')
			f = upload_form_data['rawfile']
			
			raw_dfs = form.get_plates(f, size)
			raw_df = raw_dfs[0]

			barcode = raw_df['Barcode'].values[0]
			form.fields['barcode'].initial = barcode

			one_col_df = pd.DataFrame({barcode:raw_df['Signal'].values}, index=raw_df['Well']).astype(float)
			plate_df = convert_to_plate_df(one_col_df)
			json_objs = convert_to_json_objs(plate_df)

			context.update({'platenum': len(raw_dfs), 'platedataraw': raw_df.to_html()
				, 'platedata': json_objs})

		if self.steps.current == 'barcode':
			confirm_form_data = self.get_cleaned_data_for_step('confirm')
			barcode = confirm_form_data['barcode']

			context.update({'barcode': barcode, 'delim':BARCODE_DELIMITER})

		return context

	def get_form_initial(self, step):
		if step == 'barcode':
			confirm_form_data = self.get_cleaned_data_for_step('confirm')
			barcode = confirm_form_data['barcode']
			items = barcode.split(BARCODE_DELIMITER)[:5]
			fields = ['cond_str', 'concen_str', 'repl_str', 'cell_str', 'lib_part_str']
			init_dict = dict(zip(fields, items))

			return self.initial_dict.get(step, init_dict)


	def done(self, form_list, form_dict, **kwargs):

		exp = form_dict['experiment'].save()
		form_dict['platelayout'].save(exp)
		psize = form_dict['platelayout'].cleaned_data['size']
		ctrls_js = json.loads(form_dict['platelayout'].cleaned_data['ctrls'])
		ctrls = parse_ctrls(ctrls_js)
		for c in ctrls:
			c.save()

		rawfile = form_dict['upload'].cleaned_data['rawfile']
		raw_dfs = form_dict['confirm'].get_plates(rawfile, psize)

		barcode_0 = form_dict['confirm'].cleaned_data['barcode']
		barcode_str_dict = form_dict['barcode'].cleaned_data
		cond_ix, concen_ix, repl_ix, cell_ix, libpart_ix = parse_barcode_parts(barcode_0, barcode_str_dict)

		rawplategroup=PlateGroup(groupname='Raw data import', 
				plate_type=PlateGroup.RAW, 
				groupdesc='Raw data import by the import wizard',
				)

		for raw_df in raw_dfs:
			barcode = raw_df['Barcode'].values[0]
			barcode_parts = barcode.split(BARCODE_DELIMITER)
			########## HACK to add a space, so if ix == -1 corresponding attr will be empty  ###########
			barcode_parts.append(' ')

			one_col_df = pd.DataFrame({barcode:raw_df['Signal'].values}, index=raw_df['Well']).astype(float)
			plate_data_df = convert_to_plate_df(one_col_df)

			df_dict=plate_data_df.to_dict()
			dataset=Dataset(data=df_dict)
			dataset.save()

			plate_attrs = {'platename':barcode,
				       'condition':barcode_parts[cond_ix],
				       'concentration':barcode_parts[concen_ix],
				       'replicate':parse_repl(barcode_parts[repl_ix]),
				       'cell':barcode_parts[cell_ix],
				       'lib':barcode_parts[libpart_ix],
				       'dimension':plate_data_df.shape,
				       'data':dataset,
				       'controls':ctrls,
					}

			plate = RawPlate(**plate_attrs)
			plate.save()

			rawplategroup.plates.append(plate)
		
		
		rawplategroup.save()

		exp.plategroups.append(rawplategroup)
		exp.save()



		return HttpResponseRedirect('/')


# dummy view to read raw files
#def readraw(request):
#	if request.method == 'POST': 
#		print request.POST
#		return HttpResponse(json.dumps('dummy'))
#

		





########### views for QC actions ##############
def groupBoxQCview(request):
	'''
	this view creates data for QC box plots with scatter plots
	'''

	if request.method=='POST':
		form = BoxGroupPlates(request.POST)
		if form.is_valid():
			grpid=form.cleaned_data['groupid']
			start=form.cleaned_data['start_plate']
			end=form.cleaned_data['end_plate']
			json_data, json_labels, ctrls, groupdesc=form.get_box_json(grpid, num_plates=[start-1, end-1])

			var_dict={'box_data':json_data, 'labels':json_labels, 'ctrls':ctrls, 'groupdesc':str(groupdesc)}
			return render_to_response('rawqc_box.html', var_dict,
			      context_instance=RequestContext(request))
	else:
		grpid=request.GET.get('groupid')
		form = BoxGroupPlates(initial={'groupid':grpid})

	return render(request, 'box_choose.html', { 'form': form,})



def groupBoxWithCorr(request):
	'''
	this view creates data for QC box plots with correlation 
	'''
	if request.method=='POST':
		form = BoxGroupPlates(request.POST)
		if form.is_valid():
			grpid=form.cleaned_data['groupid']
			start=form.cleaned_data['start_plate']
			end=form.cleaned_data['end_plate']
			json_data, json_labels, ctrls, groupname=form.get_box_json(grpid, num_plates=[start-1, end-1])

			var_dict={'box_data':json_data, 'labels':json_labels}
			return render_to_response('qc_box_corr.html', var_dict,
			      context_instance=RequestContext(request))
	else:
		grpid=request.GET.get('groupid')
		form = BoxGroupPlates(initial={'groupid':grpid})

	return render(request, 'box_choose.html', { 'form': form,})



def errbarQCview(request):
	'''
	this view creates data for QC errbar plots
	'''
	if request.method=='POST':
		form = ErrbarGroupPlates(request.POST)
		if form.is_valid():
			grpids=form.cleaned_data['groups']
			err_data=form.get_mean_json(grpids)

			return render_to_response('errbarqc.html', {'errdata':err_data},
			      context_instance=RequestContext(request))
	else:
		expid=request.GET.get('expid')
		exp=Experiment.objects.get(id=expid)
		# TODO: add isRaw func to PlateGroup model func
		groups=filter(isRaw, exp.plategroups)
		
		#groups=PlateGroup.objects
		form = ErrbarGroupPlates()
		form.fields['groups'] = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple,
				choices=[(pg.id, pg.groupname+pg.groupdesc) if pg.groupdesc is not None else (pg.id, pg.groupname) for pg in groups])
	return render(request, 'errbar_choose.html', { 'form': form,})
	
	
	


	
########### views for analysing actions ##############
def normalisationview(request):
	if request.method=='POST':
		form=NormalisationForm(request.POST)
		if form.is_valid():
			grpid=form.cleaned_data['plategroupid']
			norm_method=form.cleaned_data['norm_method']
			form.normalise(norm_method, grpid)
			return HttpResponseRedirect('/')
	else:

		expid=request.GET.get('expid')
		exp=Experiment.objects.get(id=expid)
		# TODO: add isRaw func to PlateGroup model func
		groups=filter(isRaw, exp.plategroups)+filter(isSumm, exp.plategroups)
		
		#groups=PlateGroup.objects
		form = NormalisationForm()
		form.fields['plategroupid'] = forms.ChoiceField(widget=forms.RadioSelect,
				choices=[(pg.id, pg.groupname+pg.groupdesc) if pg.groupdesc is not None else (pg.id, pg.groupname) for pg in groups])
	return render(request, 'norm.html', { 'form': form,})


def summarisationview(request):
	if request.method=='POST':
		form=SummarisationForm(request.POST)
		if form.is_valid():
			nondruggrpid=form.cleaned_data['nondruggroupid']
			druggrpid=form.cleaned_data['druggroupid']
			summ_method=form.cleaned_data['summ_method']
			form.summarise(summ_method, nondruggrpid, druggrpid)
			return HttpResponseRedirect('/')
	else:

		expid=request.GET.get('expid')
		exp=Experiment.objects.get(id=expid)
		# TODO: add isRaw func to PlateGroup model func
		groups=filter(isRaw, exp.plategroups)+filter(isNorm, exp.plategroups)
		
		#groups=PlateGroup.objects
		form = SummarisationForm()
		form.fields['nondruggroupid'] = forms.ChoiceField(widget=forms.RadioSelect,
				choices=[(pg.id, pg.plate_type+pg.groupname+pg.groupdesc) if pg.groupdesc is not None else (pg.id, pg.plate_type+pg.groupname) for pg in groups])
		
		form.fields['druggroupid'] = forms.ChoiceField(widget=forms.RadioSelect,
				choices=[(pg.id, pg.plate_type+pg.groupname+pg.groupdesc) if pg.groupdesc is not None else (pg.id, pg.plate_type+pg.groupname) for pg in groups])
	return render(request, 'summ.html', { 'form': form,})


def actions(request):
	'''
	this view gets json from the action page and return forms accordingly, 
	e.g 'rankprod' then return randprod form, 'normalise' then return norm form.
	'''

	if request.method=='GET':
		action = request.GET['action']
		form = act_map[action]()
		return HttpResponse(form.as_p())
	if request.method=='POST':
		action = request.POST['action']
		setting = json.loads(request.POST['setting'])
		plates = json.loads(request.POST['plates'])
		form = act_map[action](setting)
		if form.is_valid():
			form.act(plates)
			return HttpResponse('done') 

def getcontxt(request):
	'''
	get contextualised info about a gene or compound
	'''
	if request.method=='GET':
		gene = request.GET['g_sym']
		act = request.GET['act']

		if act == 'achilles' and db.reagents.find({'gene_symbol':gene}).count()>0:
			reagents=[]
			for r in db.reagents.find({'gene_symbol':gene}):
				reagents.append(r)
			df=pd.DataFrame(reagents)
			
			gs_objs=[]
			if any(df['sol_name'] != 'nan') is True:
				for gs_id in set(df[df['sol_name'] != 'nan']['sol_name']):
					obj = db.gscore.find_one({'Name':gs_id})
					obj['_id']=str(obj['_id'])
					for smpl in [k for k in obj.keys() if k!='_id' and k!='Name' and k!='Description']:
						gs_objs.append({'sample':smpl, 'value':obj[smpl], 'gs_id':obj['Name']})

			headers = ['gene_symbol', 'shRNA', 'sol_id', 'sol_name', 'cscore', 'pval', 'qval']
			tbl=df[headers].to_html(index=False, escape=False)
			avg = df['cscore'].median()
			tbl='<h3>Achilles: reagents essentiality and consistency </h3>' + tbl + '<p>Median of consisteny score:' + str(avg) + '</p>'

			return HttpResponse(json.dumps({'tbl':tbl, 'gs':gs_objs}), content_type="application/json")



		elif act == 'tcga' and db.tcga.find({'gene':gene}).count()>0:
			objs=[]
			g_expr=db.tcga.find_one({'gene':gene})
			keys = sorted([k for k in g_expr.keys() if k!='_id' and k!='gene'])
			
			genes = []
			for i in range(0, len(keys), 2):
				genes.append(tuple(keys[i:i+2]))

			for fc, pval in genes:
				objs.append({'sample':fc.split('_')[0], 'value':g_expr[fc], 'pval':g_expr[pval], 'gs_id':g_expr['gene']})

			return HttpResponse(json.dumps(objs), content_type="application/json")


		else:
			return HttpResponse(json.dumps({'tbl':"<h4>Gene symbol " + gene + " can't be found in the context database</h4>", 'gs':[]}), content_type="application/json")


########### views for deleting actions ##############
def deleteplateview(request, plateid):
	deleteplate(plateid)
	return HttpResponseRedirect('/')


def deletegroupview(request, groupid):
	g=PlateGroup.objects.get(id=groupid)
	g.delete_all()
	g.delete()
	return HttpResponseRedirect('/')

def deleteexpview(request, expid):
	exp=Experiment.objects.get(id=expid)
	for g in exp.plategroups:
		deletegroupview(request,g.id)
	exp.delete()

	return HttpResponseRedirect('/')


########### util views ##############

def getchemblID(request):

	key=request.GET.get('key')
	inchikey=key
	url=urllib2.urlopen("https://www.ebi.ac.uk/chemblws/compounds/stdinchikey/"+inchikey+".json")
	res=json.loads(url.read())
	chemblID=res['compound']['chemblId']
	print chemblID
	return HttpResponse(chemblID, content_type="text/plain") 

def getlinks(request):
	anno_txt=request.GET.get('anno')
	screentype=request.GET.get('type')
	
	# screentype is hardcoded!!!!
	if screentype=='sirna':
		links=[{"name":"cosmic", "url":"http://cancer.sanger.ac.uk/cosmic/gene/analysis?ln="+anno_txt+"&samps=1001#ts"},
		       {"name":"cbio", "url":"http://www.cbioportal.org/cross_cancer.do?cancer_study_id=all&data_priority=0&case_ids=&gene_set_choice=user-defined-list&gene_list="+anno_txt+"&clinical_param_selection=null&tab_index=tab_visualize&Action=Submit#crosscancer/overview/0/"+anno_txt},
		       {"name":"RNAi score", "url":""},]
	else: 
		try:
			tmpurl=urllib2.urlopen("http://cactus.nci.nih.gov/chemical/structure/"+anno_txt.replace(' ', '%20')+"/stdinchikey")
			key=tmpurl.read().split('=')[1]
			url=urllib2.urlopen("https://www.ebi.ac.uk/chemblws/compounds/stdinchikey/"+key+".json")
			res=json.loads(url.read())
			chemblID=res['compound']['chemblId']

			links=[{"name":"ChEMBL","url":"https://www.ebi.ac.uk/chembl/compound/inspect/"+chemblID},]


		except urllib2.HTTPError:
			links=[{"name":"Error","url":"Couldn't resolve chemical name: "+anno_txt},]

	return render(request, 'links.html', { 'links': links,})
